# -*- coding: utf-8 -*-
"""
Module Description: schema字段属性类
Date: 2018/8/2
Author:Bai Jin Ping
"""
import sys
from datetime import datetime, date, timedelta
from time import mktime

from framework.dal.orm.orm_expection import ORMException
from common.service_config import get_orm_model_tem_long
try:
    import simplejson as json
except ImportError:
    import json
if sys.version_info.major < 3:
    str = str
    unicode = unicode
    bytes = str
    basestring = basestring
else:
    str = str
    unicode = str
    bytes = bytes
    basestring = (str, bytes)
    long = int

__all__ = ['IntegerField', 'DateTimeField', 'BooleanField',
           'DateField', 'FloatField', 'DictField',
           'ListField', 'Attribute', 'CharField',
           'TimeField', 'TimeStampField']


def _check_readonly_when_setting(instance):
    """尝试更新字段时检查是否只读模型"""
    if instance.readonly:
        raise RuntimeError('Can not update readonly model fields!')


class Attribute(object):
    """
    所有字段类型的基类
    Options
        name      -- 字段所对应的字段名
        default   -- 初始化值得属性
        primary_key -- 是否是主键，默认False

    """
    def __init__(self,
                 name=None,
                 primary_key=False,
                 default=None, **kwargs):
        self.name = name
        self.primary_key = primary_key
        self.default = default
        self.kwargs = kwargs

    def __get__(self, instance, owner):
        try:
            return getattr(instance, '_' + self.name)
        except AttributeError:
            if instance.is_new:
                # 判断如果该model是查询得来的，则不设默认值
                self.__set__(instance, self.default)
            return self.default

    def __set__(self, instance, value):
        _check_readonly_when_setting(instance)

        # 将需要更新操作的值放入_update_temp_dict字典中
        temp = getattr(instance, "_update_temp_dict")
        temp.update({str(self.name): value})
        setattr(instance, '_' + self.name, value)

    def typecast_for_read(self, value):
        """Typecasts the value for reading from MySQL."""
        # return value.decode('utf-8')
        return value

    def typecast_for_storage(self, value):
        """Typecasts the value for storing to MySQL."""
        try:
            # return unicode(value)
            return value
        except UnicodeError:
            # return value.decode('utf-8')
            return value

    def value_type(self):
        # return unicode
        return str

    def acceptable_types(self):
        return basestring

    def str_type(self):
        return "Attribute"


class CharField(Attribute):
    def __init__(self, max_length=255, **kwargs):
        super(CharField, self).__init__(**kwargs)
        self.max_length = max_length

    def check_length(self, value, instance):
        """
        校验临时长度
        用于临时修复字段超长
        """
        # 没有值时，不校验长度
        if not value:
            return True

        # 校验长度
        # 由于python2中,中文字符串长度在str和unicode中的值不一致,所以增加对字符串类型的判断
        value_length = len(value if isinstance(value, unicode) else unicode(value))
        if value_length <= self.max_length:
            return True

        # 超过mode定义长度，则校验，是否有临时设置的长度
        # TODO 前提：需要保证数据库已提前修改
        table_name = instance.__class__.__name__
        tem_long = get_orm_model_tem_long(table_name, self.name)
        if tem_long and value_length <= tem_long:
            return True

        # 超过了【mode定义和zk临时】长度
        raise OverflowError

    def __set__(self, instance, value):
        _check_readonly_when_setting(instance)

        try:
            # 校验长度
            self.check_length(value, instance)
            temp = getattr(instance, "_update_temp_dict")
            temp.update({str(self.name): value})
            setattr(instance, '_' + self.name, value)
        except OverflowError:
            raise ORMException("{} CharField Too Long".format(self.name))
        except Exception as e:
            raise ORMException(e)

    def typecast_for_storage(self, value):
        if isinstance(value, (list, tuple, dict)):
            value = json.dumps(value)
            # value = str(value)
        return value

    def typecast_for_read(self, value):
        if isinstance(value, (list, tuple, dict)):
            value = json.loads(value)
        return value


class BooleanField(Attribute):
    """Boolean类型"""
    def typecast_for_read(self, value):
        return bool(int(value))

    def typecast_for_storage(self, value):
        if value is None:
            return 0
        return 1 if value else 0

    def value_type(self):
        return bool

    def acceptable_types(self):
        return self.value_type()

    def __set__(self, instance, value):
        _check_readonly_when_setting(instance)

        # 将需要更新操作的值放入_update_temp_dict字典中
        temp = getattr(instance, "_update_temp_dict")
        temp.update({str(self.name): value})
        try:
            if not value:
                setattr(instance, '_' + self.name, False)
            else:
                setattr(instance, '_' + self.name, True)
        except Exception as e:
            raise ORMException("ListField.__set__.ERROR:" + str(e))


class IntegerField(Attribute):
    """int类型"""
    def typecast_for_read(self, value):
        return int(value)

    def typecast_for_storage(self, value):
        if value is None:
            return None
        if isinstance(value, basestring):
            return int(value)
        elif isinstance(value, (int, long)):
            return value
        else:
            raise ORMException("%s is not a integer, type is %s" % (value, type(value)))

    def value_type(self):
        return int

    def acceptable_types(self):
        return int, long

    def str_type(self):
        return "int"

    def __set__(self, instance, value):
        _check_readonly_when_setting(instance)

        # 将需要更新操作的值放入_update_temp_dict字典中
        temp = getattr(instance, "_update_temp_dict")
        try:
            if value is None:
                temp.update({str(self.name): value})
                setattr(instance, '_' + self.name, None)
            else:
                temp.update({str(self.name): int(value)})
                setattr(instance, '_' + self.name, int(value))
        except Exception as e:
            raise ORMException("IntegerField.__set__.ERROR:" + str(e))


class FloatField(Attribute):
    """float类型"""
    def typecast_for_read(self, value):
        return float(value)

    def typecast_for_storage(self, value):
        if value is None:
            return None
        return value

    def value_type(self):
        return float

    def acceptable_types(self):
        return self.value_type()

    def __set__(self, instance, value):
        _check_readonly_when_setting(instance)

        # 将需要更新操作的值放入_update_temp_dict字典中
        temp = getattr(instance, "_update_temp_dict")
        try:
            if value is None:
                temp.update({str(self.name): value})
                setattr(instance, '_' + self.name, None)
            else:
                temp.update({str(self.name): float(value)})
                setattr(instance, '_' + self.name, float(value))
        except Exception as e:
            raise ORMException("FloatField.__set__.ERROR:" + str(e))


class DateTimeField(Attribute):
    """
    datetime类型
    可接受datetime类型和'%Y-%m-%d %H:%M:%S'格式的字符串
    """
    def __init__(self, auto_now=False, auto_now_add=False, **kwargs):
        super(DateTimeField, self).__init__(**kwargs)
        self.auto_now = auto_now
        self.auto_now_add = auto_now_add

    def typecast_for_read(self, value):
        try:
            return datetime.strptime(value, '%Y-%m-%d %H:%M:%S')
        except TypeError:
            return None

    def typecast_for_storage(self, value):
        if value is None:
            return None
        value = datetime_format(value)
        if value is None:
            return None
        return value.strftime('%Y-%m-%d %H:%M:%S')

    def value_type(self):
        return datetime

    def acceptable_types(self):
        return self.value_type()

    def __get__(self, instance, owner):
        try:
            value = getattr(instance, '_' + self.name)
            # print "value,instance, owner:", value, instance, owner
            if isinstance(value, (str, unicode)):
                return datetime_format(value)
            return value
        except AttributeError:
            if instance.is_new:
                self.__set__(instance, self.default)
            return self.default


class DateField(Attribute):
    """日期类型"""
    def __init__(self, auto_now=False, auto_now_add=False, **kwargs):
        super(DateField, self).__init__(**kwargs)
        self.auto_now = auto_now
        self.auto_now_add = auto_now_add

    def typecast_for_read(self, value):
        try:
            return datetime.strptime(value, '%Y-%m-%d').date()
        except TypeError:
            return None

    def typecast_for_storage(self, value):
        if value is None:
            return None
        value = date_format(value)
        if value is None:
            return None
        return value.strftime('%Y-%m-%d')

    def value_type(self):
        return date

    def acceptable_types(self):
        return self.value_type()

    def __get__(self, instance, owner):
        try:
            value = getattr(instance, '_' + self.name)
            # print "value,instance, owner:", value, instance, owner
            if isinstance(value, (str, unicode)):
                return date_format(value)
            return value
        except AttributeError:
            if instance.is_new:
                self.__set__(instance, self.default)
            return self.default


class TimeField(Attribute):
    """时间类型"""
    def __init__(self, auto_now=False, auto_now_add=False, **kwargs):
        super(TimeField, self).__init__(**kwargs)
        self.auto_now = auto_now
        self.auto_now_add = auto_now_add

    def typecast_for_read(self, value):
        try:
            return datetime.strptime(value, '%H:%M:%S').time()
        except TypeError:
            return None

    def typecast_for_storage(self, value):
        if value is None:
            return None
        value = time_format(value)
        if value is None:
            return None
        return value.strftime('%H:%M:%S.%f')

    def value_type(self):
        return datetime.time

    def acceptable_types(self):
        return self.value_type()

    def __get__(self, instance, owner):
        try:
            value = getattr(instance, '_' + self.name)
            # print "value,instance, owner:", value, instance, owner
            if isinstance(value, (str, unicode)):
                return time_format(value)
            return value
        except AttributeError:
            if instance.is_new:
                self.__set__(instance, self.default)
            return self.default


class TimeStampField(Attribute):
    """时间戳格式为10位整形加5位浮点型"""
    def __init__(self, auto_now=False, auto_now_add=False, **kwargs):
        super(TimeStampField, self).__init__(**kwargs)
        self.auto_now = auto_now
        self.auto_now_add = auto_now_add

    def typecast_for_read(self, value):
        try:
            return datetime.strptime(value, '%Y-%m-%d %H:%M:%S.%f')
        except TypeError:
            return None

    def typecast_for_storage(self, value):

        if isinstance(value, (int, float)):
            # value = datetime_format(value)
            strtime = float_to_strtime(value)
        elif isinstance(value, (str, unicode)):
            dt = timestamp_format(value)
            strtime = dt.strftime('%Y-%m-%d %H:%M:%S.%f')
        elif isinstance(value, datetime):
            strtime = value.strftime('%Y-%m-%d %H:%M:%S.%f')

        if value is None:
            return None
        return strtime

    def value_type(self):
        return float

    def acceptable_types(self):
        return self.value_type()

    def __get__(self, instance, owner):
        try:
            value = getattr(instance, '_' + self.name)
            # print "value,instance, owner:", value, instance, owner
            if isinstance(value, (str, unicode)):
                dt = timestamp_format(value)
                value = datetime_to_float(dt)
            return value
        except AttributeError:
            if instance.is_new:
                self.__set__(instance, self.default)
            return self.default


class DictField(Attribute):
    """dict类型"""

    def typecast_for_storage(self, value):
        value = json.dumps(value)
        # value = str(value)
        return value

    def typecast_for_read(self, value):
        value = json.loads(value)
        return value

    def acceptable_types(self):
        return dict

    def __get__(self, instance, owner):
        try:
            js = getattr(instance, '_' + self.name)

            # 将需要更新操作的对象放入_update_temp_dict字典中
            temp = getattr(instance, "_update_temp_dict")
            if str(self.name) not in temp:
                temp.update({str(self.name): js})
                setattr(instance, '_' + self.name, js)

            if isinstance(js, (str, unicode)):
                return json.loads(js)
            return js
        except AttributeError:
            if instance.is_new:
                self.__set__(instance, self.default)
            return self.default

    def __set__(self, instance, value):
        _check_readonly_when_setting(instance)

        # 将需要更新操作的值放入_update_temp_dict字典中
        temp = getattr(instance, "_update_temp_dict")
        temp.update({str(self.name): value})
        try:
            if not value:
                setattr(instance, '_' + self.name, {})
            else:
                if isinstance(value, (str, unicode)):
                    setattr(instance, '_' + self.name, json.loads(value))
                else:
                    setattr(instance, '_' + self.name, value)
        except Exception as e:
            raise ORMException("DictField__set__ERROR:" + str(e))


class ListField(Attribute):
    """list类型"""

    def typecast_for_storage(self, value):
        value = json.dumps(value)
        return value

    def typecast_for_read(self, value):
        return json.loads(value)

    def acceptable_types(self):
        return list

    def __get__(self, instance, owner):
        try:
            js = getattr(instance, '_' + self.name)

            # 将需要更新操作的对象放入_update_temp_dict字典中
            temp = getattr(instance, "_update_temp_dict")
            if str(self.name) not in temp:
                temp.update({str(self.name): js})
                setattr(instance, '_' + self.name, js)

            if isinstance(js, (str, unicode)):
                return json.loads(js)
            return js
        except AttributeError:
            if instance.is_new:
                self.__set__(instance, self.default)
            return self.default

    def __set__(self, instance, value):
        _check_readonly_when_setting(instance)

        # 将需要更新操作的值放入_update_temp_dict字典中
        temp = getattr(instance, "_update_temp_dict")
        temp.update({str(self.name): value})
        try:
            if not value:
                setattr(instance, '_' + self.name, [])
            else:
                if isinstance(value, (str, unicode)):
                    setattr(instance, '_' + self.name, json.loads(value))
                else:
                    setattr(instance, '_' + self.name, value)
        except Exception as e:
            raise ORMException("ListField.__set__.ERROR:" + str(e))


def to_unicode(x):
    if isinstance(x, bytes) is False:
        return str(x)
    # return x.decode('utf-8')
    return str(x)


def datetime_format(*args, **kwargs):
    """用于格式化日期时间"""
    if len(args) == 1:
        if type(args[0]) == bytes:
            arg = to_unicode(args[0])
        else:
            arg = args[0]
        if type(arg) in (str, unicode) and (
                    arg.isdigit() or len([1 for x in arg.split('.') if x.isdigit()]) == 2):
            tmp = datetime.fromtimestamp(float(arg))
        elif type(arg) in (int, float):
            tmp = datetime.fromtimestamp(float(arg))
        elif issubclass(arg.__class__, datetime):
            tmp = arg
        else:
            if '.' in arg:
                arg = arg[:arg.index('.')]
            tmp = datetime.strptime(arg, '%Y-%m-%d %H:%M:%S')

        return datetime(tmp.year, tmp.month, tmp.day, tmp.hour, tmp.minute, tmp.second)

    else:
        return datetime(*args, **kwargs)


def date_format(*args, **kwargs):
    """用于格式化日期"""
    if len(args) == 1:
        if type(args[0]) == bytes:
            arg = to_unicode(args[0])
        else:
            arg = args[0]
        if type(arg) in (str, unicode):
            # tmp = datetime.fromtimestamp(float(arg))
            tmp = datetime.strptime(arg, '%Y-%m-%d')
        elif issubclass(arg.__class__, datetime):
            return arg
        else:
            return arg

        return datetime(tmp.year, tmp.month, tmp.day).date()
    else:
        return datetime(*args, **kwargs).date()


def time_format(*args, **kwargs):
    """用于格式化日期"""
    if len(args) == 1:
        if type(args[0]) == bytes:
            arg = to_unicode(args[0])
        else:
            arg = args[0]
        if type(arg) in (str, unicode):
            # tmp = datetime.fromtimestamp(float(arg))
            if '.' in arg:
                tmp = datetime.strptime(arg, '%H:%M:%S.%f')
                # return datetime(tmp.hour, tmp.minute, tmp.second, tmp.microsecond).time()
            else:
                # 错误
                tmp = datetime.strptime(arg, '%H:%M:%S')
                # return datetime(tmp.hour, tmp.minute, tmp.second).time()
            return tmp.time()

        elif issubclass(arg.__class__, datetime):
            return arg
        else:
            return arg
    else:
        return datetime(*args, **kwargs).time()


def timestamp_format(*args, **kwargs):
    """用于格式化时间戳"""
    if len(args) == 1:
        if type(args[0]) == bytes:
            arg = to_unicode(args[0])
        else:
            arg = args[0]
        if type(arg) in (str, unicode):
            # tmp = datetime.fromtimestamp(float(arg))
            if '.' in arg:
                tmp = datetime.strptime(arg, '%Y-%m-%d %H:%M:%S.%f')
                # return datetime(tmp.hour, tmp.minute, tmp.second, tmp.microsecond).time()
            else:
                tmp = datetime.strptime(arg, '%Y-%m-%d %H:%M:%S')
                # return datetime(tmp.hour, tmp.minute, tmp.second).time()
            return tmp

        elif issubclass(arg.__class__, datetime):
            return arg
        else:
            return arg
    else:
        return datetime(*args, **kwargs)


def float_to_strtime(ftime):
    """int/float转datetime字符串"""
    if isinstance(ftime, (int, float)):
        date_array = datetime.utcfromtimestamp(ftime)
        date_array = date_array + timedelta(hours=8)
        return date_array.strftime("%Y-%m-%d %H:%M:%S.%f")
    else:
        raise TypeError('传入参数类型错误')


def datetime_to_float(dt):
    """datetime转float"""
    if isinstance(dt, datetime):
        timetuple = datetime.timetuple(dt)
        return mktime(timetuple) + dt.microsecond/1000000.0
    else:
        raise TypeError('传入参数类型错误')


INDEX = (IntegerField, DateTimeField, DateField, FloatField,
         DictField, ListField, Attribute, CharField, TimeField, TimeStampField)
